package xj.util.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JInternalFrame;
import javax.swing.JLayeredPane;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

import org.jdesktop.swingx.JXPanel;

/**
 * A class used to popup a JPanel.
 */

public class JPanelPopup
  extends JXPanel 
  implements Popup, java.io.Serializable {

  int desiredLocationX, desiredLocationY;

  public JPanelPopup() {
    this(true);
  }

  public JPanelPopup(boolean opaque) {
    super();
    setOpaque(opaque);
    setLayout(new BorderLayout());
    setDoubleBuffered(true);
    //this.setOpaque(false);
    //this.setBackground(new Color(0, 0, 0, 0));
  }

  public Component getComponent() {
    return this;
  }

  public void addComponent(Component aComponent, Object constraints) {
    this.add(aComponent, constraints);
  }

  public void removeComponent(Component c) {
    this.remove(c);
  }

  @Override
  public void update(Graphics g) {
    paint(g);
  }

  public void pack() {
    setSize(getPreferredSize());
  }

  public void showPopup(Component invoker, int timeout) {
    showPopup(invoker);
    if (timeout > 0) {
      int delay = timeout * 1000;
      ActionListener taskPerformer = new ActionListener() {
	public void actionPerformed(ActionEvent evt) {
	  hidePopup();
	}
      };
      Timer timer = new Timer(delay, taskPerformer);
      timer.setRepeats(false);
      timer.start();
    }
  }

  private Component invoker; 

  public void showPopup(Component invoker) {
    this.invoker = invoker; 
    if (invoker != null) { 
      SwingUtilities.invokeLater(new Runnable() {
	  public void run() { 
	    _showPopup();
	  }
	});
    }
  }

  private void _showPopup() {
    if (debug) { 
      System.out.println("JPanelPopup.showPopup()"); 
    }

    Container parent = null;
    if (invoker != null) { 
      parent = invoker.getParent();
    }
    for (Container p = parent; p != null; p = p.getParent()) {
      if (p instanceof JRootPane) {
	if (p.getParent() instanceof JInternalFrame) {
	  continue;
	}
	parent = ((JRootPane) p).getLayeredPane();
	for (p = parent.getParent(); p != null && (!(p instanceof java.awt.Window)); 
	     p = p.getParent()) {
	  ;
	}
	break;
      } else if (p instanceof Window) {
	parent = p;
	break;
      }
    }
    Point p = SwingUtilities.convertPoint(null, desiredLocationX, desiredLocationY, parent);
    this.setLocation(p.x, p.y);
    if (parent instanceof JLayeredPane) {
      if (debug) { 
	System.out.println("JPanelPopup.showPopup() parent is JLayeredPane"); 
      }
      ((JLayeredPane) parent).add(this, JLayeredPane.POPUP_LAYER, 0);
    } else { 
      if (debug) { 
	System.out.println("JPanelPopup.showPopup() parent is other"); 
      }
      parent.add(this);
    }
  }

  public void hidePopup() {
    SwingUtilities.invokeLater(new Runnable() {
	public void run() { 
	  _hidePopup();
	}
      });
  }

  private void _hidePopup() {
    Container parent = getParent();
    Rectangle r = this.getBounds();
    if (parent != null) {
      parent.remove(this);
      parent.repaint(r.x, r.y, r.width, r.height);
    }
  }

  public Rectangle getBoundsOnScreen() {
    Container parent = getParent();
    if (parent != null) {
      Rectangle r = getBounds();
      Point p;
      p = convertParentLocationToScreen(parent, r.x, r.y);
      r.x = p.x;
      r.y = p.y;
      return r;
    } else {
      throw new Error("getBoundsOnScreen called on an invisible popup");
    }
  }

  Point convertParentLocationToScreen(Container parent, int x, int y) {
    Window parentWindow = null;
    Rectangle r;
    Container p;
    Point pt;
    for (p = this; p != null; p = p.getParent()) {
      if (p instanceof Window) {
	parentWindow = (Window) p;
	break;
      }
    }
    if (parentWindow != null) {
      r = parentWindow.getBounds();
      pt = new Point(x, y);
      pt = SwingUtilities.convertPoint(parent, pt, null);
      pt.x += r.x;
      pt.y += r.y;
      return pt;
    } else {
      throw new Error("convertParentLocationToScreen: no window ancestor found");
    }
  }

  Point convertScreenLocationToParent(Container parent, int x, int y) {
    Window parentWindow = null;
    for (Container p = parent; p != null; p = p.getParent()) {
      if (p instanceof Window) {
	parentWindow = (Window) p;
	break;
      }
    }
    if (parentWindow != null) {
      Point p = new Point(x, y);
      SwingUtilities.convertPointFromScreen(p, parent);
      return p;
    } else {
      throw new Error("convertScreenLocationToParent: no window ancestor found");
    }
  }

  public void setLocationOnScreen(int x, int y) {
    Container parent = getParent();
    if (parent != null) {
      Point p = convertScreenLocationToParent(parent, x, y);
      this.setLocation(p.x, p.y);
    } else {
      desiredLocationX = x;
      desiredLocationY = y;
    }
  }

  public void setParentLocation(Container parent, int x, int y) {
    if (parent != null) {
      Point ps = SwingUtilities.convertPoint(parent, x, y, null);
      desiredLocationX = ps.x;
      desiredLocationY = ps.y;
    } else {
      desiredLocationX = x;
      desiredLocationY = y;
    }
  }

  protected static final boolean debug = false;

}
